#include <cassert>
#include <cstdlib>

#include "alvo.hpp"

framestack_t *
enter_frame (	ALVO_runtime	& frame, 
				A_LAZY			& LAZY, 
				framestack_t	* old_frame) {    //ALVO_frame??
	framestack_t *ret = new framestack_t;
	ret->parent    = &frame;
	ret->foobar    = &LAZY;
	ret->old_frame = old_frame;
	return ret;
}

ALVO*
eval_lazy (ALVO_runtime &frame, A_LAZY  *i, framestack_t *calling_stack);

ALVO*
eval (ALVO_runtime &frame, ALVO * /*const*/ i, framestack_t *calling_stack) {

//	return i->eval(frame);
	
	if (i->ac == AC_LAZY) {
		A_LAZY *gr = (A_LAZY*)i;
		return eval_lazy (frame, gr, calling_stack);
	}else	if (i->ac == AC_INTEGER) {
		A_INT &gr = (A_INT&)*i;
		frame.push(&gr);
	}else	if (i->ac == AC_EVAL) {
		A_EVAL &gr = (A_EVAL&)*i;
		frame.eval(&gr);
	}else {
		//~ cout << "\nerror(32)" << i->ac << endl;
		//~ assert(32!=32);
		throw 77;
	}

	return NULL;
}

ALVO*
eval (ALVO_runtime &frame, ALVO * /*const*/ i) {
	ALVO *x;
	x= eval(frame, i, NULL);
	return x;
}

ALVO_C& c(ALVO_runtime &B);
ALVO_X& x(ALVO_runtime &B);

ALVO *
alvo_x(ALVO_runtime &B) {
	A_LAZY* A1=c(B).lazy();
	A_INT*  A2=c(B).int_(3, x(B).cont(*A1));
	A_EVAL* A3=c(B).eval("print", x(B).cont(*A1));
	//
	return A1;
}

void items_push_back(void *y,ALVO*aa);

void A_LAZY::add(ALVO*aa) {items_push_back(this,aa);}
void A_LIST::add(ALVO*aa) {items_push_back(this,aa);}
void  A_GRP::add(ALVO*aa) {items_push_back(this,aa);}

AH_CONT* ALVO_X::cont(A_CONT_TAG& ag) {
	AH_CONT* R=new AH_CONT;
	R->valid=true;
	R->c=&ag;
	return R;
}
A_GRP*  ALVO_C::group() { A_GRP * R=new A_GRP;  return R; }
A_LIST*  ALVO_C::list() { A_LIST* R=new A_LIST; return R; }
A_LAZY*  ALVO_C::lazy() { A_LAZY* R=new A_LAZY; return R; }

A_INT*   ALVO_C::int_(char* ais, AH_CONT*ak) {
	return int_(atoi(ais), ak);
}

A_INT*   ALVO_C::int_(int   ai, AH_CONT*ak) {
	A_INT* R=new A_INT;
	R->m=ai;
	assert(ak->valid);
	ak->c->add(R);
	return R;
}

AH_CONT& operator<< (AH_CONT &ak, ALVO &av) {
	ak.c->add(&av);
	return ak;
}

A_STR*   ALVO_C::stringular(char *as, AH_CONT*ak) {
	A_STR* R=new A_STR;
	R->m=as;
	assert(ak->valid);
	ak->c->add(R);
	// ak << R; // TODO: change method sig
	return R;
}

A_SYM*   ALVO_C::symbol(char *as, AH_CONT*ak) {
	A_SYM* R=new A_SYM;
	R->m=as;
	assert(ak->valid);
	ak->c->add(R);
	return R;
}

A_EVAL* ALVO_C::eval(char* ac, AH_CONT*ak) {
	A_EVAL* R=new A_EVAL;
	R->m=ac;
	assert(ak->valid);
	ak->c->add(R);
	return R;
}

ALVO_runtime* /*ALVO_runtime::*/base = new ALVO_runtime ();

void ALVO_runtime::push(ALVO * aa) {
//	prv->items.push_back(aa);
	items_push_back(this,aa);
}

extern "C" {extern int rest(char*,void*);}

int main(void) {
	ALVO_runtime *B=new ALVO_runtime;
	ALVO *C=alvo_x(*B);
	eval(*B,C);

	ALVO *CC=0;
	rest("fib11.alvo", &CC);

	ALVO*CCC=CC;
	eval(*B,CCC);
	//
	return 0;
}
